using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.Serialization;

namespace Shrapnel
{
	/// <summary>
	/// A base class for an element that can be linked to other elements
	/// </summary>
	[DataContract()]
	public abstract class Node : Element
	{
		/// <summary>
		/// All connectors
		/// </summary>
		[DataMember(Name="Connectors")]
		private List<Connector> connectors = new List<Connector>();


		/// <summary>
		/// Access to our list of connectors
		/// </summary>
		public List<Connector> Connectors
		{
			get { return connectors; }
		}


		/// <summary>
		/// Draw our node and all its connectors. Intended to be overridden, since
		/// it won't draw anything by itself.
		/// </summary>
		/// <param name="graphics">Graphics object to use for drawing</param>
		/// <param name="viewState">ViewState to use for drawing</param>
		public override void Draw(Graphics graphics, ViewState viewState)
		{
			foreach (Connector connector in connectors)
			{
				connector.Draw(graphics, viewState);
			}
		}


		/// <summary>
		/// Calculate the bounding rectangle for this node
		/// </summary>
		/// <returns>Bounding rectangle in world coordinates</returns>
		public override BoundingBox GetBoundingBox()
		{
			BoundingBox bbox = base.GetBoundingBox();

			foreach (Connector connector in connectors)
			{
				bbox.Union(connector.OccupiedArea.AreaBoundingBox);
			}

			return bbox;
		}



		/// <summary>
		/// See if we overlap with a box
		/// </summary>
		/// <param name="rectangle"></param>
		/// <returns></returns>
		public override bool OverlapsWith(BoundingBox bbox)
		{
			if (base.OverlapsWith(bbox))
				return true;

			foreach (Connector connector in connectors)
			{
				if (connector.OccupiedArea.OverlapsWith(bbox))
				{
					return true;
				}
			}

			return false; // no overlap at all
		}


		/// <summary>
		/// Find out which connectors overlap with the provided bounding box, and 
		/// return the first one we find.
		/// </summary>
		/// <returns>First found connector that overlaps with provided box, or null when none overlaps</returns>
		public Connector FindFirstOverlappingConnector(BoundingBox box)
		{
			foreach (Connector connector in connectors)
			{
				if (connector.OccupiedArea.OverlapsWith(box))
				{
					return connector;
				}
			}

			return null;
		}



		/// <summary>
		/// Move our element to the provided position, so that our area's topleftposition is equal to that position.
		/// Also move our connectors too.
		/// </summary>
		/// <param name="position">Position to move to in world space</param>
		protected override void MoveToPosition(PointF position)
		{
			PointF moveDelta = new PointF(
				position.X - this.Position.X,
				position.Y - this.Position.Y);

			// Move our element
			base.MoveToPosition(position);

			// Move our connectors
			foreach (Connector connector in connectors)
			{
				connector.OccupiedArea.MoveToPosition(new PointF(
					connector.OccupiedArea.TopLeftPosition.X + moveDelta.X,
					connector.OccupiedArea.TopLeftPosition.Y + moveDelta.Y));
			}
		}


		/// <summary>
		/// Determine if a new cable is allowed to be connected to a connector owned by this node.
		/// This function should be overriden by the user to apply constraints to the cables that 
		/// can be wired. This function must return true for both nodes that are connected to each other
		/// before the cable will be created.
		/// 
		/// This default implementation will only disallow cables from a connector to the same connector.
		/// </summary>
		/// <param name="childConnector">The connector on this node</param>
		/// <param name="otherConnector">The other connector that the new cable will be connected to</param>
		/// <param name="otherNode">The other connector's owner node (can be the same as this node)</param>
		/// <returns></returns>
		public virtual bool AllowNewCableAtConnector(
			Connector childConnector,
			Connector otherConnector,
			Node otherNode)
		{
			// By default we only disallow cables from ourself to ourself...
			return (otherConnector != childConnector);
		}

	}
}
